﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace Kurax.BoardGames.Server {
    public class Server {
        private bool isStopPending;
        private readonly List<Connection> connections;

        public int Port {
            get;
            private set;
        }

        public bool IsRunning {
            get;
            private set;
        }

        private void EstablishConnection(TcpClient client) {
            var connection = new Connection(client);
            lock(this.connections)
                this.connections.Add(connection);
            connection.Run();
            lock(this.connections)
                this.connections.Remove(connection);
        }

        private void StartClientListener() {
            IAsyncResult asyncResult = null;
            var listener = new TcpListener(IPAddress.Any, this.Port);
            listener.Start();

            while(true) {
                // 判断是否已经停止服务器
                if(this.isStopPending) {
                    listener.Stop();
                    break;
                }
                //等待客户端连接
                if(asyncResult == null)
                    asyncResult = listener.BeginAcceptTcpClient(result => {
                        asyncResult = null;
                        if(this.isStopPending)
                            return;
                        try {
                            //创建客户端线程
                            var thread = new Thread(client => this.EstablishConnection((TcpClient)client));
                            thread.Start(listener.EndAcceptTcpClient(result));
                        } catch(ObjectDisposedException) {
                            return;
                        }
                    }, null);

                Thread.Sleep(200);
            }
        }

        private void StartPolicyAccessListener() {
            const string POLICY_REQUEST_STRING = "<policy-file-request/>";

            IAsyncResult asyncResult = null;
            var listener = new TcpListener(IPAddress.Any, 943);
            listener.Start();

            while(true) {
                // 判断是否已经停止服务器
                if(this.isStopPending) {
                    listener.Stop();
                    break;
                }
                //等待客户端连接
                if(asyncResult == null)
                    asyncResult = listener.BeginAcceptTcpClient(result => {
                        asyncResult = null;
                        if(this.isStopPending)
                            return;
                        try {
                            //创建客户端线程
                            var client = listener.EndAcceptTcpClient(result);
                            var stream = client.GetStream();
                            var buffer = new byte[POLICY_REQUEST_STRING.Length];
                            var total = 0;
                            while(total < buffer.Length)
                                total += stream.Read(buffer, total, buffer.Length - total);
                            var text = Encoding.ASCII.GetString(buffer);
                            if(string.Compare(POLICY_REQUEST_STRING, text, true) == 0) {
                                var fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "clientaccesspolicy.xml");
                                if(File.Exists(fileName)) {
                                    var policyXml = File.ReadAllText(fileName);
                                    policyXml = policyXml.Replace("[AllowFrom]", Dns.GetHostAddresses(Dns.GetHostName()).Where(ip => ip.AddressFamily == AddressFamily.InterNetwork && !ip.ToString().StartsWith("192.168.")).Any() ? "http://zyd.kurax.net" : "*");
                                    buffer = Encoding.ASCII.GetBytes(policyXml);
                                    stream.Write(buffer, 0, buffer.Length);
                                }
                            }
                            client.Close();
                        } catch(ObjectDisposedException) {
                            return;
                        }
                    }, null);

                Thread.Sleep(200);
            }
        }

        public Server(int port = 4533) {
            this.Port = port;
            this.connections = new List<Connection>();
        }

        public void Start() {
            if(this.IsRunning)
                throw new InvalidOperationException("Server is already running.");

            Console.WriteLine("Server is starting...");

            this.IsRunning = true;
            this.isStopPending = false;

            var policyAccessListenerThread = new Thread(this.StartPolicyAccessListener);
            policyAccessListenerThread.Start();

            this.StartClientListener();

            policyAccessListenerThread.Join(5000);

            this.IsRunning = false;
            this.isStopPending = false;
        }

        public void Stop() {
            if(!this.IsRunning)
                return;

            Console.WriteLine("Server is stopping...");

            this.isStopPending = true;
            lock(this.connections)
                foreach(var networkProcessor in this.connections)
                    networkProcessor.Stop();

            while(this.IsRunning)
                Thread.Sleep(200);
        }
    }
}
